#include "eldrawer.h"
#include <QKeyEvent>
#include <QPaintEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QTimer>
#include "../basic/elbutton.h"
#include "../basic/eltheme.h"

ElDrawer::ElDrawer(QWidget *parent)
    : ElComponent{parent}
{
    _mask.setAlphaF(0.3f);
    resize(parent->size());
    _d = new ElDrawerPrivate(this);
    setDirection(Rtl);
    setShowClose(true);
    setFocusPolicy(Qt::StrongFocus);
    initPalette();
    hide();
}

QString ElDrawer::title() const
{
    return _d->_title;
}

void ElDrawer::setTitle(const QString &title)
{
    if (_d->_title == title)
        return;
    _d->_title = title;
}

QWidget *ElDrawer::content() const
{
    return _d->_content;
}

void ElDrawer::setContent(QWidget *content)
{
    if (_d->_content == content)
        return;
    // content is left to the user to destroy.
    _d->_content = content;
    _d->_content->hide();
    _d->_content->setParent(_d);
}

qreal ElDrawer::drawerSize(Qt::SizeMode sizeMode) const
{
    return sizeMode == Qt::AbsoluteSize ? _d->width() : _percentSize;
}

void ElDrawer::setDrawerSize(qreal sz, Qt::SizeMode sizeMode)
{
    _sizeMode = sizeMode;
    auto p = dynamic_cast<QWidget *>(parent());
    if (sizeMode == Qt::AbsoluteSize) {
        if (_direction == Rtl || _direction == Ltr) {
            _d->resize(sz, p->height());
            _percentSize = sz / p->width() * 100;
        } else {
            _d->resize(p->width(), sz);
            _percentSize = sz / p->height() * 100;
        }
    } else {
        _percentSize = sz;
        if (_direction == Rtl || _direction == Ltr)
            _d->resize(p->width() * _percentSize * 0.01, p->height());
        else
            _d->resize(p->width(), p->height() * _percentSize * 0.01);
    }
}

ElDrawer::CloseBehavior ElDrawer::closeBehavior() const
{
    return _closeBehavior;
}

void ElDrawer::setCloseBehavior(CloseBehavior closeBehavior)
{
    if (_closeBehavior == closeBehavior)
        return;
    _closeBehavior = closeBehavior;
}

ElDrawer::Direction ElDrawer::direction() const
{
    return _direction;
}

void ElDrawer::setDirection(Direction direction)
{
    _direction = direction;
    auto p = dynamic_cast<QWidget *>(parent());
    if (_direction == Rtl || _direction == Ltr) {
        _d->resize(p->width() * _percentSize * 0.01, p->height());
    } else {
        _d->resize(p->width(), p->height() * _percentSize * 0.01);
    }
}

QColor ElDrawer::maskColor() const
{
    return _mask;
}

void ElDrawer::setMaskColor(const QColor &maskColor)
{
    if (_mask == maskColor)
        return;
    _mask = maskColor;
}

bool ElDrawer::hasModal() const
{
    return _modal;
}

void ElDrawer::setModal(bool modal)
{
    if (_modal == modal)
        return;
    _modal = modal;
}

bool ElDrawer::isShowClose() const
{
    return _showClose;
}

void ElDrawer::setShowClose(bool showClose)
{
    _showClose = showClose;
    if (_showClose) {
        _d->createCloseBtn(this);
    } else {
        delete _d->_closeBtn;
        _d->_closeBtn = nullptr;
    }
}

bool ElDrawer::isCloseOnPressEsc() const
{
    return _closeOnPressEsc;
}

void ElDrawer::setCloseOnPressEsc(bool closeOnPressEsc)
{
    if (_closeOnPressEsc == closeOnPressEsc)
        return;
    _closeOnPressEsc = closeOnPressEsc;
}

bool ElDrawer::isMaskClosable() const
{
    return _maskClosable;
}

void ElDrawer::setMaskClosable(bool maskClosable)
{
    if (_maskClosable == maskClosable)
        return;
    _maskClosable = maskClosable;
}

void ElDrawer::fadeOut()
{
    _d->fadeOut();
    if (_closeBehavior == Delete) {
        QTimer::singleShot(_d->_animOpa->duration(), Qt::CoarseTimer, this, &ElDrawer::deleteLater);
    } else {
        QTimer::singleShot(_d->_animOpa->duration(), Qt::CoarseTimer, this, &ElDrawer::hide);
    }
}

void ElDrawer::adjustfySize()
{
    // update geometry
    auto p = dynamic_cast<QWidget *>(parent());
    resize(p->size());
    if (_sizeMode == Qt::RelativeSize)
        setDrawerSize(_percentSize);
    auto points = _d->calcPos();
    _d->move(points.second);
    // update animation
    _d->updatePosAnimation();
}

void ElDrawer::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    _modal ? painter.setBrush(_mask) : painter.setBrush(Qt::NoBrush);
    painter.setPen(Qt::NoPen);
    painter.drawRect(rect());
    e->accept();
}

void ElDrawer::showEvent(QShowEvent *e)
{
    setFocus(); // only focus being set can it receives keyboard input.
    raise();
    _d->show();
    e->accept();
}

void ElDrawer::mouseReleaseEvent(QMouseEvent *e)
{
    if (_maskClosable) {
        auto pos = e->pos();
        auto geo = _d->geometry();
        int leftBoundary = 0, rightBoundary = width(), topBoundary = 0, bottomBoundary = height();
        switch (_direction) {
        case Rtl:
            rightBoundary = geo.x();
            break;
        case Ltr:
            leftBoundary = geo.right();
            break;
        case Ttb:
            topBoundary = geo.bottom();
            break;
        case Btt:
            bottomBoundary = geo.y();
            break;
        default:
            break;
        }
        if (pos.x() > leftBoundary && pos.x() < rightBoundary && pos.y() > topBoundary
            && pos.y() <= bottomBoundary)
            emit closedBefore();
    }
    e->accept();
}

void ElDrawer::keyPressEvent(QKeyEvent *e)
{
    if (_closeOnPressEsc && e->key() == Qt::Key_Escape) {
        emit closedBefore();
    }
    e->accept();
}

void ElDrawer::initPalette()
{ /*nothing*/
}

const int ElDrawerPrivate::Padding_Lr = 20;
const int ElDrawerPrivate::Padding_Tb = 25;

ElDrawerPrivate::ElDrawerPrivate(ElDrawer *parent)
    : ElComponent{parent}
{
    initAnimation();
    initPalette();
}

void ElDrawerPrivate::paintEvent(QPaintEvent *e)
{
    QPainter painter(this);
    // draw title
    QRectF br;
    if (!_title.isEmpty()) {
        painter.drawText(QRectF(), Qt::AlignCenter, _title, &br);
        painter.setBrush(_palette.color(ElPalette::Background));
        painter.setPen(Qt::NoPen);
        painter.drawRect(rect());
        painter.setPen(_palette.color(ElPalette::Foreground));
        auto font = painter.font();
        font.setPointSize(12);
        painter.setFont(font);
        QRectF drawingRect(Padding_Lr, 0, width(), br.height() + 2 * Padding_Tb);
        painter.drawText(drawingRect, Qt::AlignVCenter | Qt::AlignLeft, _title);
    }
    // show close button
    if (_closeBtn != nullptr) {
        _closeBtn->move(width() - Padding_Lr - _closeBtn->width(), Padding_Tb);
    }
    // show content
    if (_content != nullptr) {
        _content->resize(width() - Padding_Lr,
                         height() - 2 * Padding_Tb
                             - (_title.isEmpty() ? 0 : br.height() + Padding_Tb));
        _content->move(Padding_Lr, Padding_Tb + (_title.isEmpty() ? 0 : br.height() + Padding_Tb));
        _content->show();
    }
    e->accept();
}

void ElDrawerPrivate::showEvent(QShowEvent *e)
{
    _animOpa->setDirection(QAbstractAnimation::Forward);
    _animOpa->start();
    updatePosAnimation();
    _animPos->setDirection(QAbstractAnimation::Forward);
    _animPos->start();
    e->accept();
}

void ElDrawerPrivate::hideEvent(QHideEvent *e)
{
    if (_content != nullptr) {
        _content->hide();
    }
    e->accept();
}

void ElDrawerPrivate::initPalette()
{
    _palette.setColor(ElPalette::Foreground, ThemeColor::textPrimaryColor);
}

void ElDrawerPrivate::createCloseBtn(ElDrawer *parent)
{
    _closeBtn = new ElTextButton(this);
    _closeBtn->setIcon(":/icons/close.svg");
    _closeBtn->setClipped(true);
    connect(_closeBtn, &ElTextButton::clicked, this, [=] { emit parent->closedBefore(); });
}

void ElDrawerPrivate::initAnimation()
{
    _animOpa = new QPropertyAnimation(this, "opacity", this);
    _animOpa->setDuration(300);
    _animOpa->setEasingCurve(QEasingCurve::OutQuad);
    _animOpa->setStartValue(0);
    _animOpa->setEndValue(1);
    _animPos = new QPropertyAnimation(this, "pos", this);
    _animPos->setDuration(300);
    _animPos->setEasingCurve(QEasingCurve::OutQuad);
    // the setting of _animX's start value and end value will be put into updatePosAnimation()
}

void ElDrawerPrivate::updatePosAnimation()
{
    auto points = calcPos();
    _animPos->setStartValue(points.first);
    _animPos->setEndValue(points.second);
}

std::pair<QPoint, QPoint> ElDrawerPrivate::calcPos()
{
    auto p = dynamic_cast<ElDrawer *>(parent());
    int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
    switch (p->direction()) {
    case ElDrawer::Rtl:
        x1 = p->width();
        x2 = p->width() - width();
        break;
    case ElDrawer::Ltr:
        x1 = -width();
        x2 = 0;
        break;
    case ElDrawer::Ttb:
        y1 = -height();
        y2 = 0;
        break;
    case ElDrawer::Btt:
        y1 = p->height();
        y2 = p->height() - height();
        break;
    default:
        break;
    }
    return std::make_pair(QPoint(x1, y1), QPoint(x2, y2));
}

void ElDrawerPrivate::fadeOut()
{
    _animOpa->setDirection(QAbstractAnimation::Backward);
    _animOpa->start();
    _animPos->setDirection(QAbstractAnimation::Backward);
    _animPos->start();
}
